package com.example.caoben_back.service.impl;

import com.example.caoben_back.entity.po.Post;
import com.example.caoben_back.service.IPostService;
import com.example.caoben_back.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class PostCacheService {

    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private IPostService postService;

    private static final String HOT_POST_LIST_KEY = "hot_post_list";
    private static final String POST_DETAIL_KEY_PREFIX = "post_detail:";

    /**
     * 获取热门帖子列表（带缓存）
     *
     * @param page 页码
     * @param size 每页大小
     * @return 帖子列表
     */
    public List<Post> getHotPostList(int page, int size) {
        String key = HOT_POST_LIST_KEY + ":" + page + ":" + size;
        List<Post> posts = (List<Post>) redisUtil.get(key);

        if (posts == null) {
            try {
                // 使用互斥锁解决缓存击穿问题
                String lockKey = "lock:" + key;
                if (redisUtil.set(lockKey, "1", 10)) { // 10秒锁过期时间
                    try {
                        // 缓存未命中，查询数据库
                        posts = postService.getHotPosts(page, size);

                        // 写入缓存，设置过期时间（防止缓存雪崩，设置随机过期时间）
                        long expireTime = 300 + ThreadLocalRandom.current().nextLong(300); // 5-10分钟随机过期时间
                        redisUtil.set(key, posts, expireTime);

                        // 同时将每个帖子详情也缓存起来（防止缓存穿透）
                        cachePostDetails(posts);
                    } finally {
                        // 释放锁
                        redisUtil.del(lockKey);
                    }
                } else {
                    // 获取锁失败，短暂等待后重新尝试从缓存获取
                    Thread.sleep(100);
                    return getHotPostList(page, size);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        return posts;
    }

    /**
     * 获取帖子详情（带缓存）
     *
     * @param postId 帖子ID
     * @return 帖子详情
     */
    public Post getPostDetail(Integer postId) {
        String key = POST_DETAIL_KEY_PREFIX + postId;
        Object postObj = redisUtil.get(key);

        if (postObj == null) {
            try {
                // 使用互斥锁解决缓存击穿问题
                String lockKey = "lock:" + key;
                if (redisUtil.set(lockKey, "1", 10)) { // 10秒锁过期时间
                    try {
                        // 缓存未命中，查询数据库
                        Post post = postService.getById(postId);

                        if (post != null) {
                            // 写入缓存
                            // 防止缓存穿透，即使数据为null也缓存（设置较短过期时间）
                            redisUtil.set(key, post, 600); // 10分钟过期
                            return post;
                        } else {
                            // 数据库中也没有数据，缓存空值（防止缓存穿透）
                            redisUtil.set(key, "NULL_VALUE", 300); // 5分钟过期
                            return null;
                        }
                    } finally {
                        // 释放锁
                        redisUtil.del(lockKey);
                    }
                } else {
                    // 获取锁失败，短暂等待后重新尝试从缓存获取
                    Thread.sleep(100);
                    return getPostDetail(postId);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        } else if ("NULL_VALUE".equals(postObj)) {
            // 缓存的是空值，表示数据库中也没有数据（防止缓存穿透）
            return null;
        }

        return (Post) postObj;
    }

    /**
     * 缓存帖子详情列表
     *
     * @param posts 帖子列表
     */
    private void cachePostDetails(List<Post> posts) {
        for (Post post : posts) {
            String key = POST_DETAIL_KEY_PREFIX + post.getPostId();
            // 使用较短的过期时间，避免占用过多内存
            // 防止缓存雪崩，设置随机过期时间
            long expireTime = 300 + ThreadLocalRandom.current().nextLong(300); // 5-10分钟随机过期时间
            redisUtil.set(key, post, expireTime);
        }
    }

    /**
     * 更新帖子后清除相关缓存
     *
     * @param postId 帖子ID
     */
    public void clearPostCache(Integer postId) {
        // 清除帖子详情缓存
        redisUtil.del(POST_DETAIL_KEY_PREFIX + postId);

        // 清除热门帖子列表缓存（简单起见，清除所有分页缓存）
        Set<String> keys = redisTemplate.keys(HOT_POST_LIST_KEY + "*");
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 更新热门帖子列表缓存
     */
    public void updateHotPostListCache() {
        // 清除旧的热门帖子列表缓存
        Set<String> keys = redisTemplate.keys(HOT_POST_LIST_KEY + "*");
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
        
        // 预热新的热门帖子列表缓存（可选）
        getHotPostList(1, 10);
    }
}